package com.lx.web;

import com.lx.core.ApplicationContext;
import com.lx.core.annotation.Param;
import com.lx.core.annotation.RequestBody;
import com.lx.util.LX;
import com.lx.core.LXLog;
import com.lx.core.annotation.Bean;
import com.lx.core.annotation.Request;
import com.lx.util.LXJson;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

@Bean(init = "init")
public class DispatchServlet extends HttpServer.Servlet {

    //说明: 记录接口入口
    /**{ ylx } 2021/9/11 22:41 */
    private Map<String, Mapping> mappings = new HashMap<>();

    //说明: 初始化
    /**{ ylx } 2021/9/11 22:37 */
    public void init(ApplicationContext ac){
        LXLog.debug("开始扫描接口注解");
        ac.getIoc().values().forEach(bean->{
            if (bean.getClass().isAnnotationPresent(Request.class)){
                LXLog.debug("扫描到接口类:"+bean);
                Class<?> cls = bean.getClass();
                Request req = cls.getAnnotation(Request.class);
                Arrays.asList(cls.getDeclaredMethods()).forEach(method -> {
                    if (method.isAnnotationPresent(Request.class)){
                        String me = method.getAnnotation(Request.class).value();
                        String mapping = (req.value().startsWith("/")?"":"/")+req.value()+(me.startsWith("/")?me:"/"+me);
                        Mapping m = new Mapping(mapping,method, bean,req.requestType());
                        LXLog.debug("扫描到接口:"+m);
                        LX.exMsg(mappings.containsKey(mapping),"接口重复:"+mappings.get(mapping)+" -- "+m);
                        mappings.put(mapping,m);
                    }
                });

            }
        });
    }
    @Override
    public HttpServer.Response doPost(HttpServer.Request request) throws Exception {
        LXLog.info("开始调用接口:"+request);
        HttpServer.Response response = null;
        String path = request.getUrl().replaceAll("/+","/");
        Mapping mapping = null;
        if ((mapping = mappings.get(path))!= null && mapping.containsRequestType(request.getRequestType())){
            Object res = invokeMethod(mapping, request);
            response = new HttpServer.Response("200",LX.toJSONString(res));
        }else{
            response = new HttpServer.Response("404",null);
        }
        LXLog.info("结束调用接口:"+response);
        return response;
    }

    /** 说明: 调用接口
     * @author ylx 2023/6/30 17:15 */
    private Object invokeMethod(Mapping mapping, HttpServer.Request request) throws InvocationTargetException, IllegalAccessException {
        Parameter[] parameters = mapping.method.getParameters();
        if (parameters.length>0){
            Object [] objs = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                Param p = null;
                if (parameter.getAnnotation(RequestBody.class) != null){
                    if (LX.isNotEmpty(request.getBody())){
                        objs[i] = LXJson.toMap(request.getBody());
                    }
                }else if ((p = parameter.getAnnotation(Param.class))!=null){
                    objs[i] = LXJson.toObj(parameter.getType(),request.getParameter().get(p.value()));
                }
            }
            return mapping.method.invoke(mapping.object,objs);//调用初始化方法
        }else{
            return mapping.method.invoke(mapping.object);//调用初始化方法
        }
    }

    //说明: 接口信息
    /**{ ylx } 2021/9/11 22:40 */
    public class Mapping{
        private String path;
        private Method method;
        private Object object;
        private Request.RequestType[] requestType;

        public Mapping(String path,Method method, Object object,Request.RequestType[] requestType) {
            this.path = path.replaceAll("/+","/");
            this.method = method;
            this.object = object;
        }

        /** 说明: 接口方法是否有该请求类型
         * @author ylx 2023/6/29 17:22 */
        public boolean containsRequestType(String type){
            if (requestType == null || requestType.length == 0){
                return true;
            }
            for (Request.RequestType requestType1 : requestType) {
                if (requestType1.name().equals(type)){
                    return true;
                }
            }
            return false;
        }

        @Override
        public String toString() {
            return "Mapping{" +
                    "path='" + path + '\'' +
                    ", requestType='"+requestType+"'"+
                    ", method=" + method +
                    ", object=" + object +
                    '}';
        }
    }

}
